#include "irq.h"
#include "os_cfg.h"
#include "cpu_instr.h"
#include "syscall.h"

static gate_desc_t idt_table[IDT_TABLE_NR] __attribute__((section(".data")));

void gate_desc_set(gate_desc_t *desc, uint16_t selector, uint32_t offset, uint16_t attr)
{
    desc->offset_low  = offset & 0xFFFF;
    desc->selector    = selector;
    desc->attr        = attr;
    desc->offset_high = (offset >> 16) & 0xFFFF;
}
void irq_init(void)
{
    for (int i = 0; i < IDT_TABLE_NR; i++)
    {
        gate_desc_set(&idt_table[i],
                      KERNEL_SELECTOR_CS,
                      (uint32_t)exception_handler_unknown,
                      INT_GATE_P | INT_GATE_DPL0 | INT_GATE_TYPE32);
    }
    irq_install(IRQ_UNKNOWN, (irq_handler_t)exception_handler_unknown);
    irq_install(IRQ0_DE_DIVIDE_ERROR, (irq_handler_t)exception_handler_divide_error);
    irq_install(IRQ1_DB_DBG, (irq_handler_t)exception_handler_debug);
    irq_install(IRQ2_NMI, (irq_handler_t)exception_handler_nmi);
    irq_install(IRQ3_BP_BREAKPOINT, (irq_handler_t)exception_handler_breakpoint);
    irq_install(IRQ4_OF_OVERFLOW, (irq_handler_t)exception_handler_overflow);
    irq_install(IRQ5_BR_BOUND_RANGE, (irq_handler_t)exception_handler_bound_range_exceeded);

    irq_install(IRQ6_UD_INVALID_OP, (irq_handler_t)exception_handler_invalid_opcode);
    irq_install(IRQ7_NM_DEVICE_NOT_AVAILABLE, (irq_handler_t)exception_handler_device_not_available);
    irq_install(IRQ8_DF_DOUBLE_FAULT, (irq_handler_t)exception_handler_double_fault);
    irq_install(IRQ10_TS_INVALID_TSS, (irq_handler_t)exception_handler_invalid_tss);
    irq_install(IRQ11_NP_SEG_NOT_PRESENT, (irq_handler_t)exception_handler_segment_not_present);
    irq_install(IRQ12_SS_STACK_FAULT, (irq_handler_t)exception_handler_stack_segment_fault);
    irq_install(IRQ13_GP_PROTECTION, (irq_handler_t)exception_handler_general_protection_fault);
    irq_install(IRQ14_PF_PAGE_FAULT, (irq_handler_t)exception_handler_page_fault);

    irq_install(IRQ16_MF_MATH_FAULT, (irq_handler_t)exception_handler_fpu_fault);
    irq_install(IRQ17_AC_ALIGNMENT_CHECK, (irq_handler_t)exception_handler_alignment_check);
    irq_install(IRQ18_MC_MACHINE_CHECK, (irq_handler_t)exception_handler_machine_check);
    irq_install(IRQ19_XM_SIMD_FLOATING_POINT, (irq_handler_t)exception_handler_simd_floating_point);
    irq_install(IRQ20_VE_VIRTUALIZATION, (irq_handler_t)exception_handler_virtualization);
    irq_install(IRQ21_CP_CONTROL_PROTECTION, (irq_handler_t)exception_handler_control_protection);

    gate_desc_set(idt_table + 0x80,
                  KERNEL_SELECTOR_CS,
                  (uint32_t)exception_handler_syscall,
                  INT_GATE_P | INT_GATE_DPL3 | INT_GATE_TYPE32);
    // irq_install(0x80,(irq_handler_t)exception_handler_syscall);

    lidt((uint32_t)idt_table, sizeof(idt_table));
    pic_init();
}
int irq_install(int irq_num, irq_handler_t handler)
{
    if (irq_num < 0 || irq_num >= IDT_TABLE_NR)
    {
        return -1;
    }
    gate_desc_set(&idt_table[irq_num],
                  KERNEL_SELECTOR_CS,
                  (uint32_t)handler,
                  INT_GATE_P | INT_GATE_DPL0 | INT_GATE_TYPE32);
    return 0;
}

irq_state_t irq_enter_protect(void)
{
    irq_state_t state = read_eflags();
    irq_disable_global();
    return state;
}

void irq_exit_protect(irq_state_t state)
{
    write_eflags(state);
}
